﻿#region using
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Schema;

using XmlGenSharp.Logic.Main.Configuration;
#endregion

namespace XmlGenSharp.Logic.Main
{
  public class BindingAnalyzer
  {
    // Constants

    #region Constants
    private const string ClassName = "BindingAnalyzer";
    #endregion

    // Constructors

    #region BindingAnalyzer(string rootDir, string inputFileName, string defaultNamespace)
    public BindingAnalyzer(string rootDir, string inputFileName, string defaultNamespace)
    {
      RootDir = rootDir;
      InputFileName = inputFileName;
      DefaultNamespace = defaultNamespace;
    }
    #endregion

    // Public Properties

    #region RootDir
    public string RootDir
    {
      get;
      protected set;
    }
    #endregion

    #region InputFileName
    public string InputFileName
    {
      get;
      protected set;
    }
    #endregion

    #region Schema
    public XmlSchema Schema
    {
      get;
      private set;
    }
    #endregion

    #region SchemaSet
    public XmlSchemaSet SchemaSet
    {
      get;
      private set;
    }
    #endregion

    #region DefaultNamespace
    public string DefaultNamespace
    {
      get
      {
        return _defaultNamespace;
      }
      set
      {
        _defaultNamespace = value;
      }
    }
    private string _defaultNamespace = "NotSpecified";
    #endregion

    #region Job
    public Job Job
    {
      get;
      private set;
    }
    #endregion

    #region Namespace
    public string Namespace
    {
      get
      {
        if (Job != null && Job.CLRInfo != null && Job.CLRInfo.Namespace != null)
          return Job.CLRInfo.Namespace;

        return DefaultNamespace;
      }
    }
    #endregion

    #region Config
    public Config Config
    {
      get;
      set;
    }
    #endregion

    // Public Methods

    #region Analyze(IAnalyzerContext cx)
    public void Analyze(IAnalyzerContext cx)
    {
      LoadConfig();

      if (!File.Exists(InputFileName))
        throw new FileNotFoundException(InputFileName + " not found");

      if (Path.GetExtension(InputFileName).ToLower() == ".xsd")
        AnalyzeFromXsd(cx);
      else if (Path.GetExtension(InputFileName).ToLower() == ".xenum")
        AnalyzeFromEnumerationInfo(cx);
      else
        AnalyzeFromJobFile(cx);
    }
    #endregion

    #region AnalyzeFromJobFile(IAnalyzerContext cx)
    public void AnalyzeFromJobFile(IAnalyzerContext cx)
    {
      try
      {
        Job = new Job(InputFileName);

        string fullPath = Job.Schema.File;
        if (!Path.IsPathRooted(fullPath))
          fullPath = Path.Combine(Path.GetDirectoryName(InputFileName), fullPath);

        XmlTextReader reader = new XmlTextReader(fullPath);
        Schema = XmlSchema.Read(reader, ValidationCallback);
        SchemaSet = new XmlSchemaSet();
        SchemaSet.Add(Schema);
        SchemaSet.Compile();

        cx.OnBeforeAnalyzeFromJobFile();
        AnalyzeAll(cx);
        cx.OnAfterAnalyzeFromJobFile();
      }
      catch (Exception ex)
      {
        string msg = String.Format("Could not load job from file '{0}'", InputFileName);
        cx.AddError(ClassName, "AnalyzeJobFromFile", msg);
        cx.AddError(ClassName, "AnalzyeJobFromFile", ex.Message);
      }
    }
    #endregion

    #region AnalyzeFromXsd(IAnalyzerContext cx)
    public void AnalyzeFromXsd(IAnalyzerContext cx)
    {
      try
      {
        XmlTextReader reader = new XmlTextReader(InputFileName);
        Schema = XmlSchema.Read(reader, ValidationCallback);
        SchemaSet = new XmlSchemaSet();
        SchemaSet.Add(Schema);
        SchemaSet.Compile();

        cx.OnBeforeAnalyzeFromXsd();
        AnalyzeAll(cx);
        cx.OnAfterAnalyzeFromXsd();
      }
      catch (Exception ex)
      {
        string msg = String.Format("Error loading XML schema file: '{0}'", InputFileName);
        cx.AddError(ClassName, "AnalyzeFromXsd", msg);
        cx.AddError(ClassName, "AnalyzeFromXsd", ex.Message);
      }
    }
    #endregion

    #region AnalyzeFromEnumerationInfo(IAnalyzerContext cx)
    public void AnalyzeFromEnumerationInfo(IAnalyzerContext cx)
    {
      try
      {
        EnumerationInfo enumInfo = new EnumerationInfo(InputFileName);
        AdvancedEnumAnalyzer aea = new AdvancedEnumAnalyzer(this, enumInfo);
        aea.Analyze(cx);
      }
      catch (Exception ex)
      {
        string msg = String.Format("Could not load job from file '{0}'", InputFileName);
        cx.AddError(ClassName, "AnalyzeFromEnumerationInfo", msg);
        cx.AddError(ClassName, "AnalyzeFromEnumerationInfo", ex.Message);
      }
    }
    #endregion

    // Private Methods

    #region LoadConfig()
    private void LoadConfig()
    {
      string configFile = ConfigLocator.Locate(RootDir, InputFileName);

      if (configFile == null || !File.Exists(configFile))
      {
        Config = new Config();
        return;
      }

      try
      {
        Config = new Config(configFile);
      }
      catch
      {
      }
    }
    #endregion

    #region AnalyzeAll(IAnalyzerContext cx)
    private void AnalyzeAll(IAnalyzerContext cx)
    {
      try
      {
        foreach (XmlSchemaObject obj in Schema.SchemaTypes.Values)
        {
          try
          {
            if (obj is XmlSchemaComplexType)
              cx.FoundComplexType(obj as XmlSchemaComplexType);
            else if (obj is XmlSchemaSimpleType)
              cx.FoundSimpleType(obj as XmlSchemaSimpleType);
          }
          catch (Exception ex)
          {
            cx.AddError(ClassName, "AnalyzeAll", ex.Message);
          }
        }
      }
      catch (Exception ex)
      {
        cx.AddError(ClassName, "AnalyzeAll", ex.Message);
      }
    }
    #endregion

    #region ValidationCallback(object sender, ValidationEventArgs args)
    private void ValidationCallback(object sender, ValidationEventArgs args)
    {
      // if (args.Severity == XmlSeverityType.Warning)
      // Console.Write("WARNING: ");
      // else if (args.Severity == XmlSeverityType.Error)
      // Console.Write("ERROR: ");

      // Console.WriteLine(args.Message);
    }
    #endregion
  }
}
